ここでは、読者のプログラムの中で使うことのできるちょっとした Flex 定義を一覧にして示します。これらの多くは、このマニュアルを読んだあとでは、かなり自明なはずです。しかし、読者がこうしたコードを最初から作らなくてもよいようにするために、ここに入れてあります。
***ページの 4.1 節「Flex と C」において述べたように、コメントは input() を使って処理することができます。これをおこなうためのコードは以下のようになります。
a = input();
while(a != EOF){
b = input();
if(a =='*' && b == '/'){
break;
}else{
a = b;
}
}
if(a == EOF){
error_message("EOF in comment");
}
}
文字列は、それが入力として与えられたとき破棄されないという点で、コメントとは若干異なります。しかし、基本的なアプローチは同じです。第1の方法としては、input() を使って文字列を処理することができます。そのコードは以下のようになります。
%(
#include <stdio.h>
#include <malloc.h>
#include <ctype.h>
#define ALLOC_SIZE 32 /* バッファの(再)割り当て用 */
#define isodigit(x) ((x) >= '0' && (x) <= '7')
#define hextoint(x) (isdigit((x)) ? (x) - '0'\
: ((x) - 'A') + 10)
void yyerror(char *message)
{
printf("\nError: %s\n",message);
}
%}
%%
\" {
int inch,count,max_size;
char *buffer;
int temp;
buffer = malloc(ALLOC_SIZE);
max_size = ALLOC_SIZE;
inch = input();
count = 0;
while(inch != EOF && inch != '"' &&
inch != '\n'){
if(inch == '\\'){
inch = input();
switch(inch){
caes '\n': inch
= input(); break;
case 'b' : inch
= '\b'; break;
case 't' : inch
= '\t'; break;
case 'n' : inch
= '\n'; break;
case 'v' : inch
= '\v'; break;
case 'f' : inch
= '\f'; break;
case 'r' : inch
= '\r'; break;
case 'X' :
case 'x' : inch
= input();
if(isxdigit(inch)){
temp = hextoint(toupper(inch));
inch = input();
if(isxdigit(inch)){
temp = (temp << 4) +
hextoint(toupper(inch));
} else {
unput(inch);
}
inch = temp;
} else {
unput(inch);
inch = 'x';
}
break;
default:
if(isodigit(inch)){
temp = inch - '0';
inch = input();
if(isodigit(inch)){
temp = (temp << 3) + (inch - '0');
} else {
unput(inch);
goto done;
}
inch = input();
if(isodigit(inch)){
temp = (temp << 3) + (inch - '0');
} else {
unput(inch);
}
done:
inch = temp;
}
}
}
buffer[count++] = inch;
if(count >= max_size){
buffer = realloc(buffer,max_size
+ ALLOC_SIZE);
max_size +=
ALLOC_SIZE;
}
inch = input();
}
if(inch == EOF || inch == '\n'){
yyerror("Unterminated string.");
}
buffer[count] = '\0';
printf("String = \"%s\"\n",buffer);
free(buffer);
}
.
\n
%%
%{
#include <ctype.h>
#define isodigit(x) ((x) >= '0' && (x) <= '7')
#define hextoint(x) (isdigit((x)) ? (x) - '0' \
: ((x) - 'A') + 10)
char *buffer = NULL;
int buffer_size = 0;
void yyerror(char *message)
{
printf("\nError: %s\n",message);
}
%}
%x STRING
hex (x|X)[0-9a-fA-F]{1,2}
oct [0-7]{1,3}
%%
\"
{
buffer = malloc(1);
buffer_size = 1; strcpy(buffer,"");
BEGIN(STRING);
}
<STRING>\n
{
yyerror("Unterminated string");
free(buffer);
BEGIN(INITIAL);
}
<STRING><<EOF>> {
yyerror("EOF in string");
free(buffer);
BEGIN(INITIAL):
}
<STRING>[^\\\n"] {
buffer_size += yyleng;
buffer = realloc(buffer,buffer_size+1);
strcat(buffer,yytext);
}
<STRING>\\\n /*
エスケープされた改行を無視する */
<STRING>\\{hex} {
int temp = 0,loop = 0, foo;
for(loop=yyleng-2; loop>0; loop--){
temp <<= 4;
foo = toupper(yytext[yyleng-loop]);
temp += hextoint(foo);
}
buffer = relloc(buffer,buffer_size+1);
buffer[buffer_size-1] = temp;
buffer[buffer_size] = '\0';
buffer_size += 1;
}
<STRING>\\{oct} {
int temp =0,loop =0;
for(loop=yyleng-1; loop>0; loop--){
temp <<=3;
temp += (yytext[yyleng-loop] - '0');
}
buffer = realloc(buffer,buffer_size+1);
buffer[buffer_size-1] = temp;
buffer[buffer_size] = '\0';
buffer_size += 1;
}
<STRING>\\[^\n] {
buffer = realloc(buffer,buffer_size+1);
switch(yytext[yyleng-1]){
case 'b' : buffer[buffer_size-1] = '\b';
break;
case 't' : buffer[buffer_size-1] = '\t';
break;
case 'n' : buffer[buffer_size-1] = '\n';
break;
case 'v' : buffer[buffer_size-1] = '\v';
break;
case 'f' : buffer[buffer_size-1] = '\f';
break;
case 'r' : buffer[buffer_size-1] = '\r';
break;
default : buffer[buffer_size-1] =
yytext[yyleng-1];
}
buffer[buffer_size] = '\0';
buffer_size += 1;
}
<STRING>\"
{
printf("string = \"%s\"",buffer);
free(buffer);
BEGIN(INITIAL);
}
%%
ここでは、C に見られる様々な数値形式に対してよく使われる定義をいくつか示し、さらにその使い方の実例を1つ示します。注目すべき主要な点は、数の値を獲得するために scanf() を使っている点とオーバーフローが発生しないよう long 型の値をスキャンするデフォルトのルールです。一般的には、yytext を数に変換する最良の方法は sscanf() を使うことです。
%{
#include <stdio.h>
#define UNSIGNED_LONG_SYM 1
#define SIGNED_LONG_SYM 2
#define UNSIGNED_SYM 3
#define SIGNED_SYM 4
#define LONG_DOUBLE_SYM 5
#define FLOAT_SYM 6
union _yylval {
long double ylong_double;
float yfloat;
unsigned long yunsigned_long;
unsigned yunsigned;
long ysigned_long;
int ysigned;
} yylval;
%}
digit
[0-9]
hex_digi t [0-9a-fA-F]
oct_digit
[0-7]
exponent [eE][+-]?{digit}+
i
{digit}+
float_constant ({i}\.{i}?|{i}?\.{i}){exponent}?
hex_constant 0[xX]{hex_digit}+
oct_constant 0{oct_digit}*
int_constant {digit}+
long_ext
[lL]
unsigned_ext [uU]
float_ext [fF]
ulong_ext [lL][uU]|[uU][lL]
%%
{hex_constant}{ulong_ext} { /* 0x の部分をスキップする
*/
sscanf(&yytext[2],"%lx",
&yylval.yunsigned_long);
return(UNSIGNED_LONG_SYM);
}
{hex_constant}{long_ext}
{
sscanf(&yytext[2],"%lx",
&yylval.ysigned_long);
return(SIGNED_LONG_SYM);
}
{hex_constant}{unsigned_ext} {
sscanf(&yytext[2],"%x",
&yylval.yunsigned);
return(UNSIGNED_SYM);
}
{hex_constant}
{ /* オーバーフローを回避するために %lx を使う */
sscanf(&yytext[2],"%lx",
&yylval.ysigned_long);
return(SIGNED_LONG_SYM);
}
{oct_constant}{ulong_ext} {
sscanf(yytext,"%lo",
&yylval.yunsigned_long);
return(UNSIGNED_LONG_SYM);
}
{oct_constant}{long_ext}
{
sscanf(yytext,"%lo",
&yylval.ysigned_long);
return(SIGNED_LONG_SYM);
}
{oct_constant}{unsigned_ext} {
sscanf(yytext,"%o",
&yylval.yunsigned);
return(UNSIGNED_SYM);
}
{oct_constant}
{ /* オーバーフローを回避するために %lo を使う */
sscanf(yytext,"%lo",
&yylval.ysigned_long);
return(SIGNED_LONG_SYM);
}
{int_constant}{ulong_ext}
{
sscanf(yytext,"%ld",
&yylval.yunsigned_long);
return(UNSIGNED_LONG_SYM);
}
{int_constant}{long_ext}
{
sscanf(yytext,"%ld",
&yylval.ysigned_long);
return(SIGNED_LONG_SYM);
}
{int_constant}{unsigned_ext} {
sscanf(yytext,"%d",
&yylval.yunsigned);
return(UNSIGNED_SYM);
}
{int_constant}
{ /* オーバーフローを回避するために %ld を使う */
sscanf(yytext,"%ld",
&yylval.ysigned_long);
return(SIGNED_LONG_SYM);
}
{float_constant}{long_ext}
{
sscanf(yytext,"%lf",
&yylval.ylong_double);
return(LONG_DOUBLE_SYM);
}
{float_constant}{float_ext} {
sscanf(yytext,"%f",
&yylval.yfloat);
return(FLOAT_SYM);
}
{float_constant}
{ /* オーバーフローを回避するために %lf を使う */
sscanf(yytext,"%lf",
&yylval.ylong_double);
return(LONG_DOUBLE_SYM);
}
%%
int main(void)
{
int code;
while((code = yylex())){
printf("yytext : %s\n",yytext);
switch(code){
case UNSIGNED_LONG_SYM:
printf("Type of number : UNSIGNED LONG\n");
printf("Value of number : %lu\n",
yylval.yunsigned_long);
break;
case SIGNED_LONG_SYM:
printf("Type of number : SIGNED LONG\n");
printf("Value of number : %ld\n",
yylval.ysigned_long);
break;
case UNSIGNED_SYM:
printf("Type of number : UNSIGNED\n");
printf("Value of number : %u\n",
yylval.yunsigned);
break;
case SIGNED_SYM:
printf("Type of number : SIGNED\n");
printf("Value of number : %d\n",
yylval.ysigned);
break;
case LONG_DOUBLE_SYM:
printf("Type of number : LONG DOUBLE\n");
printf("Value of number : %lf\n",
yylval.ylong_double);
break;
case FLOAT_SYM:
printf("Type of number : FLOAT\n");
printf("Value of number : %f\n",
yylval.yfloat);
break;
default:
printf("Type of number : UNDEFINED\n");
printf("Value of number : UNDEFINED\n");
break;
}
}
return(0);
}
時には1つのプログラムの中で複数のスキャナを持つ必要がある場合がありますが、こうすると2回以上現れる関数や変数についてリンカが文句を言ってきます。スキャナとそれに関連するすべてのものの名前を変更する必要があります。すべてのスキャナ関数、マクロ、データの名前は yy もしくは YY で始まりますので、これはきわめて簡単です。しなければならないことは名前の接頭辞を変更することだけです。これは sed を使って簡単に行うことができますが、ここではおもしろ半分で、これをおこなう flex スキャナを示しましょう。
%{
#include <stdio.h>
char lower_replace[1024];
char upper_replace[1024];
%}
%%
"yy" fprintf(yyout,"%s",lower_replace);
"YY" fprintf(yyout,"%s",upper_replace);
%%
int main(argc,argv)
int argc;
char **argv;
{
if(argc < 3){
printf("Usage %s lower UPPER\n",argv[0]);
exit(1);
}
strcpy(lower_replace,argv[1]);
strcpy(upper_replace,argv[2]);
yylex();
return(0);
}
注:いくつかの Flex 内部ルーチンは将来 Flex ライブラリ(-lfl)の中に移されるでしょう。そうなるとこのテクニックは機能しなくなります。しかし、この変更がおこなわれる時には、変更する必要のある関数名を変更する方法を Flex 自身がサポートするようになるでしょう。
行数をカウントしたいのであれば、ファイルの先頭のオプションの C コード・セクションに変数を定義して、改行をチェックします。スタート状態の中でも改行をチェックするのを忘れないようにしてください。さもないと、行数のカウントはうまくいきません。例えば、以下のようにします。
yyin と yyout をリダイレクトすることによってスキャナを(したがって Bison
によるパーサをも)ソケットにアタッチすることができます。これは fdopen()
を呼ぶことによっておこないます。例えば、以下のようになります。
Copyright (C) 1992, 1993 Free Software Foundation
Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.
Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by the Free Software Foundation.
日本語訳:市川和久
Japanese translation by Kazuhisa Ichikawa (ki@home.email.ne.jp)